<h1>Filter Component Reference</h1>
<h2> Introduction </h2>
<p>
A filter is a central component in a dashboard.
Traditionally represented as a drop-down list, it is typically used as a means
to quickly filter information.
</p>
<p>
In CDE, the list of selected items is usually stored in a dashboard parameter.
Whenever the filter is updated, the parameter is read and the items in that list are marked as selected.
When the user changes the selection, the parameter is updated.
</p>

<h2>Quick Reference (for the impatient)</h2>
<p>
The main properties of the Filter Component are:
<dl>
    <dt>Title</dt>
    <dd>
        Label on top of the filter
    </dd>

    <dt>Parameter</dt>
    <dd> Parameter []
        <p>
        Name of the parameter that stores the selection state.
        </p>
        <p>
        The filter reads from the parameter when the component renders,
        and writes to the parameter whenever the user chooses to commit the selection state.
        In single-select mode, the commit is automatic, whereas in multi-select mode
        the user must first click on the &quot;Apply&quot; button
        </p>
		<p>
        The parameter is written as an array of ID's, so for a selection of "Foo" + "Bar",
        the parameter will have ["Foo", "Bar"]. In the case where only one selection is made,
        the array will have one element, ex. ["Foo"].
        </p>
    </dd>

    <dt>Multi Select</dt>
    <dd>Boolean [<code>False</code>].
        <p>
        By default, it is set to <code>False</code>, the filter only allows the selection of single items.
        If set to <code>true</code>, multiple items can be selected,
        up to the number specified by the property <code>Selection Limit</code>
        </p>
    </dd>

    <dt>Selection Limit</dt>
    <dd>Integer [<code>2000</code>]
        <p>
        Limits the number of item that can be selected.
        This property acts globally, in the sense that it does not matter which group the option belongs to.
        Only active when <code>multiselect</code> is <code>True</code>.
        </p>
    </dd>

    <dt>Show Filter</dt>
    <dd>Boolean [<code>True</code>].
        <p>
        Set to True to enable the search box.
        </p>
    </dd>

    <dt>Show Icons</dt>
    <dd>Boolean [<code>True</code>].
        <p>
        If set to True, an icon (checkbox or radio button) is shown next to each item.
        </p>
    </dd>

    <dt>Always Expanded</dt>
    <dd>Boolean [<code>False</code>].
        <p>
        Set to True to keep the filter permanently open.
        </p>
    </dd>

    <dt>Output Type</dt>
    <dd>Enum {<code>&quot;Item Ids&quot;</code> | <code>&quot;Group and Item Ids&quot;</code>}
        [<code>&quot;Item Ids&quot;</code>]
        <p>
        Determines how the selection is written to the parameter upon commit.
        If set to <code>&quot;Group and Item Ids&quot;</code>,
        the filter attemps to use groups Ids  when all of its members are selected,
        which results in a more compact result.
        </p>
    </dd>

    <dt>Value as id</dt>
    <dd>Boolean [<code>False</code>].
        <p>
        Set to True if you want the filter to output the item labels instead of the ids.
        </p>
    </dd>
</dl>
</p>

<h3>Add-Ins</h3>
<p>
The default behaviour of the component can be customized by associating a list of add-ins
to each <i>slot</i>.
The signature of the add-ins is <code>function (target, state, options)</code>,
where <code>state</code> contains two fields, <code>model</code> and <code>configuration</code>.
</p>
<p>
The available slots are:
</p>
<dl>
    <dt>postUpdate</dt>
    <dd>
        Runs when data is added to model.
        <code>target</code> is <code>null</code>.
    </dd>

    <dt>renderRootHeader</dt>
    <dd>
        Runs on every update of the Root header (the part of filter that remains visible when the filter is collapsed).
        <code>target</code> is the DOM element associated with the root container, e.g. <code>$('.filter-root-container')</code>.
    </dd>

    <dt>renderRootSelection</dt>
    <dd>
        Runs when the selection changes.
        <code>target</code> is the DOM element associated with the root container, e.g. <code>$('.filter-root-container')</code>.
    </dd>

    <dt>renderRootFooter</dt>
    <dd>
        Runs when the Root footer is updated (e.g. notifications).
        <code>target</code> is the DOM element associated with the root container, e.g. <code>$('.filter-root-container')</code>.
    </dd>

    <dt>renderGroupSelection</dt>
    <dd>
        Runs when the selection changes.
        <code>target</code> is the DOM element associated with the group container, e.g. <code>$('.filter-group-container')</code>.
    </dd>

    <dt>renderItemSelection</dt>
    <dd>
        Runs when the selection changes.
        <code>target</code> is the DOM element associated with the item container, e.g. <code>$('.filter-item-container')</code>.

    </dd>

    <dt>sortGroup</dt>
    <dd>
        Determines the visual order of the groups of items, must return a value (string or number).
        <code>target</code> is <code>null</code>.
    </dd>

    <dt>sortItem</dt>
    <dd>
        Determines the visual order of the items, must return a value (string or number).
        <code>target</code> is <code>null</code>.
    </dd>

</dl>


<h3>Advanced properties</h3>
<p>
There are also advanced properties, that allow further customization of the component:
<dl>
    <dt>Advanced Options</dt>
    <dd>
        Function that returns a javascript object. Possible properties:
        <pre>
{

    /**
     * Properties associated with importing data into the model
     */
    input: {
      indexes: { // Column indexes of the different fields (0-based)
        id: 0,           // Id is a string that identifies an item uniquely
        label: 1,        // Label is the text associated with an item
        parentId: 2,     // If the item belongs to a group of options, this is the id of the group
        parentLabel: 3,  // Label of the group to which the item belongs to.
        value: undefined // Secondary bit of text that will be show next to the item label
      },
      defaultModel: { // Default properties of the model
        isDisabled: true
      },
      root: {
        id: undefined // Overrides the id of the root.
      }
    },


    /**
     * Properties associated with exporting the state of the model to a parameter
     */
    output: {
      outputFormat: 'lowestId'  // equivalent to setting Output Type option to "Item Ids" in CDE
                                // 'highestId' is equivalent to picking "Group and Item Ids" in CDE
                                // It is also possible to assign a custom function there
    },


    /**
     * Configuration the response of the component to its environment
     */
    component: {
      pagination: {
        pageSize: Infinity // An integer sets the number of items per page. Infinity disables pagination.
      },
      search: {
        serverSide: false // Set to true only if your queries accept a search parameter
      },
      selectionStrategy: {  // Interaction logic. What actions should occur when I click on an item
        type: 'LimitedSelect', // 'SingleSelect' or 'LimitedSelect'
        limit: 500 // sets the maximum number of options that can be selected
      },

      /**
       * Configuration of the Root View
       */
      Root: {
        options: {
          styles: [], // array containing a list of CSS classes to be added to the root container
          showCommitButtons: true,   // show/hide Apply/Cancel Buttons
          showFilter: false,         // show/hide filter box
          showGroupSelection: true,  // show/hide All
          showButtonOnlyThis: false,
          showSelectedItems: false,  // optionally display the labels of selected items
          showNumberOfSelectedItems: true,
          showValue: false,         // optionally shows the value associated with the root
          scrollThreshold: 12, // number of children (items or groups) that triggers a scrollbar
          useOverlay: true, // should the filter display an overlay when expanded?
          expandMode: 'absolute' // 'relative' will push content on expand, 'absolute' will not
        },
        strings: {
          title: '',            // overrides Title property
          isDisabled: 'Unavailable', // message displayed when the filter is disabled
          allItems: 'All', // message displayed when all items are selected
          noItems: 'None', // message displayed when no items are selected
          btnApply: 'Apply', // label of the Apply button
          btnCancel: 'Cancel'  // label of the Cancel button
        },
        view: {
          overlaySimulateClick: true, // when clicking on the overlay, should the click
                                      // be delegated to the element underneath?
          scrollbar: {
            engine: 'mCustomScrollbar', // also 'optiscroll'
            options: { //engine-specific options
              theme: 'dark',
              alwaysTriggerOffsets: false,
              onTotalScrollOffset: 100
            }
          }
        }
      },

      /**
       * Configuration of the Group View
       */
      Group: {
        options: {
          showFilter: false,
          showCommitButtons: false,
          showGroupSelection: false,
          showButtonOnlyThis: false,
          showButtonCollapse: false, // show/hide button that allows collapsing of groups
          showValue: false, // optionally shows the value associated with the group
          scrollThreshold: Infinity // number of items in the group that triggers a group-only scrollbar
        },
        strings: { // Same keys than Root, but used in the context of a Group
          allItems: 'All',
          noItems: 'None',
          btnApply: 'Apply',
          btnCancel: 'Cancel'
        }
      },

      /**
       * Configuration of the Item View
       */
      Item: {
        options: {
          showButtonOnlyThis: false, //show/hide "Only This" button
          showValue: false // optionally shows the value associated with the item
        },
        strings: {
          btnOnlyThis: 'Only' // caption of the "Only this" button
        }
    }
}
        </pre>
    </dd>
</dl>
</p>


<h2>Example</h2>
<p>
The following example shows the expected data layout and demonstrates support for groups of options.
</p>


        <div class='col-xs-12'><div id='singleFilterObj_group' >
        </div></div>
        <div class='col-xs-12 last'><div id='txt_singleSelectionObj_group' >
        </div></div>
        <div class='col-xs-12'><div id='multiFilterObj_group' >
        </div></div>
        <div class='col-xs-12 last'><div id='txt_multiSelectionObj_group' >
        </div></div>

<h3>Expected data layout</h3>
<p>
By default, the filter is expecting the columns in the folowing order:
</p>
<ol>
    <li>Item Id</li>
    <li>Item Label</li>
    <li>Group Id</li>
    <li>Group Label</li>
</ol>


        <div class='col-xs-12 last'><div id='tableObj_groups' >
        </div></div>


<script language="javascript" type="text/javascript">

require([
  'cdf/Dashboard.Blueprint',
  'cdf/Logger',
  'cdf/lib/jquery',
  'amd!cdf/lib/underscore',
  'cdf/lib/moment',
  'cdf/lib/CCC/cdo',
  'cdf/dashboard/Utils',
  'cdf/components/FilterComponent',
  'cdf/components/TableComponent',
  'cdf/components/TextComponent'],
function(
  Dashboard,
  Logger,
  $,
  _,
  moment,
  cdo,
  Utils,
  FilterComponent,
  TableComponent,
  TextComponent) {

var dashboard = new Dashboard();

  dashboard.getWcdfSettings = function() {
    return {
      "author": "",
      "description": "filter_selector",
      "rendererType": "blueprint",
      "require": true,
      "style": "WDDocsRequire",
      "title": "filter_reference",
      "widget": false,
      "widgetName": "",
      "widgetParameters": []
};
  };

  dashboard.addDataSource("getData_IdValue_IdGroup", {
    dataAccessId: "getData_IdValue_IdGroup",
    path: "/public/plugin-samples/pentaho-cdf/30-documentation/30-component_reference/10-core/92-FilterComponent/Filter-Reference/filter_reference.cda"
  });

var render_singleFilter_group = new FilterComponent({
  type: "FilterComponent",
  name: "render_singleFilter_group",
  priority: 5,
  executeAtStart: true,
  htmlObject: "singleFilterObj_group",
  listeners: [],
  parameter: "singleSelectionParam_group",
  parameters: [],
  options: function(){
    return {};
},
  queryDefinition:  {
      dataSource: "getData_IdValue_IdGroup"
  },
  componentInput:  {
    valueAsId: false,
    valuesArray: []
  },
  componentOutput:  {
    outputFormat: "lowestID"
  },
  componentDefinition:  {
    title: "Single Selection",
    alwaysExpanded: false,
    multiselect: false,
    selectionLimit: 2000,
    showIcons: true,
    showButtonOnlyThis: true,
    useOverlay: true,
    showFilter: true
  },
  addIns:  {
    postUpdate: [],
    renderRootHeader: [],
    renderRootSelection: [],
    renderRootFooter: [],
    renderGroupSelection: [],
    renderItemSelection: [],
    sortGroup: [],
    sortItem: []
  }
});
var render_multiFilter_group = new FilterComponent({
  type: "FilterComponent",
  name: "render_multiFilter_group",
  priority: 5,
  executeAtStart: true,
  htmlObject: "multiFilterObj_group",
  listeners: [],
  parameter: "multiSelectionParam_group",
  parameters: [],
  options: function(){
    return {};
},
  queryDefinition:  {
      dataSource: "getData_IdValue_IdGroup"
  },
  componentInput:  {
    valueAsId: false,
    valuesArray: []
  },
  componentOutput:  {
    outputFormat: "highestID"
  },
  componentDefinition:  {
    title: "Multiple Selection",
    alwaysExpanded: false,
    multiselect: true,
    selectionLimit: 2000,
    showIcons: true,
    showButtonOnlyThis: true,
    useOverlay: true,
    showFilter: true
  },
  addIns:  {
    postUpdate: [],
    renderRootHeader: [],
    renderRootSelection: [],
    renderRootFooter: [],
    renderGroupSelection: [],
    renderItemSelection: [],
    sortGroup: [],
    sortItem: []
  }
});
dashboard.addParameter("singleSelectionParam_group", _.bind(function() { return []
}, {"dashboard": dashboard})());
dashboard.addParameter("multiSelectionParam_group", _.bind(function() { return []
}, {"dashboard": dashboard})());
var render_table_groups = new TableComponent({
  type: "TableComponent",
  name: "render_table_groups",
  priority: 5,
  htmlObject: "tableObj_groups",
  listeners: [],
  parameters: [],
  executeAtStart: true,
  extraOptions: [],
  expandParameters: [],
  expandOnClick: false,
  chartDefinition:  {
    dataSource: "getData_IdValue_IdGroup",
    colHeaders: [],
    colTypes: [],
    colFormats: [],
    colWidths: ["25%","25%","25%","25%"],
    colSortable: [],
    colSearchable: [],
    paginate: true,
    paginateServerside: false,
    paginationType: "simple_numbers",
    info: false,
    sort: false,
    sortBy: [],
    lengthChange: false,
    tableStyle: "bootstrap"
  }
});
var render_txt_singleSelection_group = new TextComponent({
  type: "TextComponent",
  name: "render_txt_singleSelection_group",
  priority: 5,
  expression: function(){

    return "Result: " + JSON.stringify(this.dashboard.getParameterValue('singleSelectionParam_group'));
} ,
  htmlObject: "txt_singleSelectionObj_group",
  listeners: ['singleSelectionParam_group'],
  executeAtStart: true
});
var render_txt_multiSelection_group = new TextComponent({
  type: "TextComponent",
  name: "render_txt_multiSelection_group",
  priority: 5,
  expression: function(){
    var selectedItems = this.dashboard.getParameterValue('multiSelectionParam_group');
    var txt = "Result: " + JSON.stringify(selectedItems) + " <br/>Selected items: <ul>";
    for( i=0 ; i < selectedItems.length; i++ ){
        txt = txt + "<li>" + selectedItems[i] + "</li>";
    }
    txt = txt + "</ul>";

    return txt;
} ,
  htmlObject: "txt_multiSelectionObj_group",
  listeners: ['multiSelectionParam_group'],
  executeAtStart: true
});

dashboard.addComponents([render_singleFilter_group, render_multiFilter_group, render_table_groups, render_txt_singleSelection_group, render_txt_multiSelection_group]);

dashboard.init();
return dashboard;
});
</script>